/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2005-2009 PRO ACTIVE SAS - www.proactive-group.com
   
  Ref_Jewel.c
  -----------

  This is the reference applications that shows how to read/write data
  in an Innovision Jewel tag.
  
  JDA 18/10/2005 : initial release
  JDA 10/01/2007 : fixed a few warnings when compiling under GCC  

*/

/*
 * This example performs the following sequence :
 * - activate a card, check it can be a Jewel
 * - Jewel Read Identification command (RID)
 * - Read the whole tag byte after byte (READ)
 * - Read the whole tag in once (RALL)
 * - Write some random data in page 2 (WRITE-E)
 * - Read the whole tag again (RALL) and check if data are OK in page 2
 */
#include "products/springprox/springprox.h"

#define RegBitFraming         0x0F
#define RegDecoderControl     0x1A
#define RegChannelRedundancy  0x22

#define PICC_REQIDL           0x26
#define PICC_REQALL           0x52

#define PCD_TRANSMIT          0x1A
#define PCD_TRANSCEIVE        0x1E

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define IN                      /* For explanation of the parameters only */
#define OUT                     /* For explanation of the parameters only */

/* Shortcut to ISO 14443-A/3 commands */

/* ---------------------------------- */
SWORD Jewel_REQA(OUT BYTE atq[2]);
SWORD Jewel_WUPA(OUT BYTE atq[2]);

/* JEWEL command set */

/* ----------------- */
SWORD Jewel_RID(OUT BYTE hr[2], OUT BYTE uid[4]);
SWORD Jewel_RALL(IN BYTE uid[4], OUT BYTE hr[2], OUT BYTE data[120]);
SWORD Jewel_READ(IN BYTE uid[4], IN BYTE address, OUT BYTE * data);
SWORD Jewel_WRITE_E(IN BYTE uid[4], IN BYTE address, IN BYTE data);
SWORD Jewel_WRITE_NE(IN BYTE uid[4], IN BYTE address, IN BYTE data);

/* JEWEL low-level command issuer */

/* ------------------------------ */
SWORD Jewel_Command(IN BYTE cmd, IN BYTE address, IN BYTE data,
                    IN BYTE uid[4], OUT BYTE * resp, IN OUT BYTE * resp_len);
SWORD Jewel_Transmit(IN BYTE which, IN BYTE value, OUT BYTE * resp,
                     IN OUT BYTE * resp_len);

/*
 the 'which' parameter can be either :
 - 0 for the first command byte
 - 1 for a command byte except the first one and the last one
 - 2 for the last command byte
*/


/*
 * Program entry
 * -------------
 */
int main(int argc, char **argv)
{
  SWORD rc;
  int i, j;
  BYTE atq[2];
  BYTE uid[4];
  BYTE hr[2];
  char s_buffer[64];
  BYTE data[120];

  char *device = NULL;

  if (argc > 1)
    device = argv[1];

  printf("SpringCard SDK for K531/SpringProx-CF/CSB Legacy\n");
  printf("\n");
  printf("INNOVISION JEWEL reference demo\n");
  printf("-------------------------------\n");
  printf("www.springcard.com\n\n");

  rc = SPROX_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get API version\n");
    goto done;
  }
  printf("API version : %s\n", s_buffer);
  printf("\n");

  /* Open reader */
  /* ----------- */

  rc = SPROX_ReaderOpen(device);
  if (rc != MI_OK)
  {
    printf("Reader not found\n");
    goto done;
  }

  rc = SPROX_ReaderGetDevice(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader found on %s\n", s_buffer);

  rc = SPROX_ReaderGetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader firwmare is %s\n", s_buffer);

  /* Configure reader in ISO 14443-A mode */
  /* ------------------------------------ */
  rc = SPROX_SetConfig(CFG_MODE_ISO_14443_A);
  if (rc != MI_OK)
  {
    printf("Failed to configure the reader\n");
    goto close;
  }

  /* RF field ON */
  SPROX_ControlRF(TRUE);

  rc = SPROX_SetConfig(CFG_MODE_ISO_14443_A);
  if (rc != MI_OK)
    goto close;

  /* Activate a card, check it can be a Jewel */
  /* ---------------------------------------- */
  rc = Jewel_REQA(atq);
  if (rc != MI_OK)
  {
    printf("No card found\n");
    goto close;
  }

  printf("ATQa received : %02X%02X\n", atq[0], atq[1]);

  rc = WriteRIC(RegChannelRedundancy, 0x20);
  if (rc != MI_OK)
    return rc;
  rc = WriteRIC(RegDecoderControl, 0x08);
  if (rc != MI_OK)
    return rc;
  rc = PcdSetTmo(4096);
  if (rc != MI_OK)
    return rc;

  /* Jewel Read Identification command (RID) */
  /* --------------------------------------- */
  rc = Jewel_RID(hr, uid);
  if (rc != MI_OK)
  {
    printf("RID failed (%d)\n", rc);
    goto close;
  }

  printf("RID received : hr=%02X%02X, uid=%02X%02X%02X%02X\n", hr[0], hr[1],
         uid[0], uid[1], uid[2], uid[4]);

  /* Read the whole tag byte after byte (READ) */
  /* ----------------------------------------- */

  printf("Reading card, please wait...\n");

  for (i = 0; i < 120; i++)
  {
    rc = Jewel_READ(uid, (BYTE) i, &data[i]);
    if (rc != MI_OK)
    {
      printf("READ(%d) failed (%d)\n", i, rc);
      goto close;
    }
  }

  /* Read the whole tag in once (RALL) */
  /* --------------------------------- */

  printf("Reading card again (faster !)...\n");

  rc = Jewel_RALL(uid, hr, data);
  if (rc != MI_OK)
  {
    printf("RALL failed (%d)\n", rc);
    goto close;
  }

  printf("RALL returned hr=%02X%02X\n", hr[0], hr[1]);
  for (i = 0; i < 120; i+=8)
  {
    printf("@%02X  ", i);
    for (j=0; j<8; j++)
      printf("%02X ", data[i+j]);
    printf(" ");
    for (j=0; j<8; j++)
      printf("%c", ((data[i+j] >= ' ') && (data[i+j] < 128)) ? data[i+j] : '.');
    printf("\n");
  }
  printf("\n");

#if 0
  /* Write some random data in page 2 (WRITE-E) */
  /* ------------------------------------------ */
  printf("Writing data, please wait...\n");

  /* Generate some random string */
  srand((unsigned int) time(NULL));
  for (i = 0; i < 8; i++)
    buffer[i] = (BYTE) rand();

  /* Write the 8-bytes string into page 2 */
  for (i = 0; i < 8; i++)
  {
    rc = Jewel_WRITE_E(uid, (BYTE) (16 + i), buffer[i]);
    if (rc != MI_OK)
    {
      printf("WRITE_E(%d) failed (%d)\n", 16 + i, rc);
      goto close;
    }
  }

  /* Read the whole tag again (RALL) and check if data are OK in page 2 */
  /* ------------------------------------------------------------------ */

  printf("Reading card again (last step)...\n");

  rc = Jewel_RALL(uid, hr, data);
  if (rc != MI_OK)
  {
    printf("RALL failed (%d)\n", rc);
    goto close;
  }

  /* Check the content of page 2 */
  if (memcmp(&data[16], &buffer[0], 8))
  {
    printf("Write/Read test failed !!!\n");
    goto close;
  }
#endif

  printf("All functions passed OK !!!\n");

close:
  /* Close the reader */
  SPROX_ControlRF(FALSE);
  SPROX_ReaderClose();

done:
  /* Display last error */
  if (rc == MI_OK)
  {
    printf("Done\n");
  } else
  {
    printf("%s (%d)\n", SPROX_GetErrorMessage(rc), rc);
  }
  return 0;
}


/*
 * JEWEL command codes
 * -------------------
 */
#define JEWEL_RID       0x78
#define JEWEL_RALL      0x00
#define JEWEL_READ      0x01
#define JEWEL_WRITE_E   0x53
#define JEWEL_WRITE_NE  0x1A

#define JEWEL_DUMMY     0x00

/*
 * Implementation of JEWEL command set
 * -----------------------------------
 */
SWORD Jewel_RID(OUT BYTE hr[2], OUT BYTE uid[4])
{
  SWORD rc;
  BYTE dummy_uid[4] = { JEWEL_DUMMY, JEWEL_DUMMY, JEWEL_DUMMY, JEWEL_DUMMY };
  BYTE resp[6];
  BYTE resp_len = sizeof(resp);

  SPROX_ControlLed(1, 1);
  rc =
    Jewel_Command(JEWEL_RID, JEWEL_DUMMY, JEWEL_DUMMY, dummy_uid, resp,
                  &resp_len);
  SPROX_ControlLed(0, 0);
  if (rc != MI_OK)
    return rc;

  if (resp_len != sizeof(resp))
    return MI_BYTECOUNTERR;

  if (hr != NULL)
    memcpy(hr, &resp[0], 2);

  if (uid != NULL)
    memcpy(uid, &resp[2], 4);

  return rc;
}

SWORD Jewel_RALL(IN BYTE uid[4], OUT BYTE hr[2], OUT BYTE data[120])
{
  SWORD rc;
  BYTE resp[122];
  BYTE resp_len = sizeof(resp);

  rc =
    Jewel_Command(JEWEL_RALL, JEWEL_DUMMY, JEWEL_DUMMY, uid, resp, &resp_len);
  if (rc != MI_OK)
    return rc;

  if (resp_len != sizeof(resp))
    return MI_BYTECOUNTERR;

  if (hr != NULL)
    memcpy(hr, &resp[0], 2);

  if (data != NULL)
    memcpy(data, &resp[2], 120);

  return rc;
}

SWORD Jewel_READ(IN BYTE uid[4], IN BYTE address, OUT BYTE * data)
{
  SWORD rc;
  BYTE resp[2];
  BYTE resp_len = sizeof(resp);

  rc = Jewel_Command(JEWEL_READ, address, JEWEL_DUMMY, uid, resp, &resp_len);
  if (rc != MI_OK)
    return rc;

  if (resp_len != sizeof(resp))
    return MI_BYTECOUNTERR;

  if (resp[0] != address)
    return MI_READERR;          /* Not the expected address in return ! */

  if (data != NULL)
    *data = resp[1];

  return rc;
}

SWORD Jewel_WRITE_E(IN BYTE uid[4], IN BYTE address, IN BYTE data)
{
  SWORD rc;
  BYTE resp[2];
  BYTE resp_len = sizeof(resp);

  rc = Jewel_Command(JEWEL_WRITE_E, address, data, uid, resp, &resp_len);
  if (rc != MI_OK)
    return rc;

  if (resp_len != sizeof(resp))
    return MI_BYTECOUNTERR;

  if (resp[0] != address)
    return MI_WRITEERR;         /* Not the expected address in return ! */

  if (resp[1] != data)
    return MI_WRITEERR;         /* Not the expected data in return ! */

  return rc;
}

SWORD Jewel_WRITE_NE(IN BYTE uid[4], IN BYTE address, IN BYTE data)
{
  SWORD rc;
  BYTE resp[2];
  BYTE resp_len = sizeof(resp);

  rc = Jewel_Command(JEWEL_WRITE_NE, address, data, uid, resp, &resp_len);
  if (rc != MI_OK)
    return rc;

  if (resp_len != sizeof(resp))
    return MI_BYTECOUNTERR;

  if (resp[0] != address)
    return MI_WRITEERR;         /* Not the expected address in return ! */

  if ((resp[1] & data) != data)
    return MI_WRITEERR;         /* Not the expected data mask in return ! */

  return rc;
}

/*
 * Alias to more usual functions
 * -----------------------------
 */

//#define PICC_REQIDL        0x26
//#define PICC_REQALL        0x52

SWORD Jewel_REQA(OUT BYTE atq[2])
{
  return Mf500PiccRequest(PICC_REQIDL, atq);
}

SWORD Jewel_WUPA(OUT BYTE atq[2])
{
  return Mf500PiccRequest(PICC_REQALL, atq);
}

/*
 * Implementation of Jewel low-level command format
 * ------------------------------------------------
 */
SWORD Jewel_Command(IN BYTE cmd, IN BYTE address, IN BYTE data,
                    IN BYTE uid[4], OUT BYTE * resp, IN OUT BYTE * resp_len)
{
  BYTE buffer[200];
  BYTE length = sizeof(buffer);
  BYTE i;
  BYTE crc[2];
  BYTE s_pos, t_pos;
  BYTE b_pos;
  SWORD rc;

  static BYTE parity_pattern[] =
    { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };

  /* Check parameters */
  /* ---------------- */
  if (uid == NULL)
    return MI_LIB_CALL_ERROR;
  if (resp == NULL)
    return MI_LIB_CALL_ERROR;
  if (resp_len == NULL)
    return MI_LIB_CALL_ERROR;

  /* Build the 7-bytes command */
  /* ------------------------- */
  buffer[0] = cmd;
  buffer[1] = address;
  buffer[2] = data;
  memcpy(&buffer[3], uid, 4);

  /* Append the CRC_B to form a 9-bytes string */
  /* ----------------------------------------- */
  SPROX_ComputeIso14443BCrc(&buffer[7], &buffer[0], 7);

#ifdef _DEBUG
  printf("-> ");
  for (i = 0; i < 9; i++)
  {
    printf("%02X ", buffer[i]);
  }
  printf("\n");
#endif

  /* Transmit the 8 first bytes of the string */
  /* ---------------------------------------- */
  for (i = 0; i < 8; i++)
  {
    /* For the first byte of the string we shall send only 7 bits */
    if (i == 0)
    {
      rc = Jewel_Transmit(0, buffer[i], NULL, NULL);
    } else
    {
      rc = Jewel_Transmit(1, buffer[i], NULL, NULL);
    }
    if (rc != MI_OK)
      return rc;
  }

  /* Transmit the last byte, and wait for the answer */
  /* ----------------------------------------------- */
  rc = Jewel_Transmit(2, buffer[8], buffer, &length);
  if ((rc != MI_OK) && (rc != MI_FRAMINGERR)) /* Ignore framing error */
    return rc;

  /* OK, now we must drop the parity bits */
  /* ------------------------------------ */

#ifdef _DEBUG
  printf("// ");
  for (i = 0; i < length; i++)
  {
    printf("%02X ", buffer[i]);
  }
  printf("\n");
#endif

  memset(resp, 0, *resp_len);

  /* Extract bytes without parity bit */
  for (t_pos = s_pos = b_pos = 0; s_pos < (length - 1); s_pos++, t_pos++)
  {
    // TRICKY TRICKY : maybe a better idea is to check the parity bits, instead of just dropping them !!!
    b_pos = s_pos % 9;
    switch (b_pos)
    {
      case 0:
        resp[t_pos] = buffer[s_pos];
        break;
      case 8:                  /* Skip this bit, this is a parity bit */
        --t_pos;
        break;
      default:
        resp[t_pos] =
          ((buffer[s_pos + 1] & parity_pattern[b_pos]) << (8 - b_pos)) |
          (buffer[s_pos] >> b_pos);
        break;
    }
  }
  *resp_len = t_pos;

#ifdef _DEBUG
  printf("<- ");
  for (i = 0; i < *resp_len; i++)
  {
    printf("%02X ", resp[i]);
  }
  printf("\n");
#endif

  if (*resp_len < 2)
  {
    /* No CRC ??? */
    return MI_BYTECOUNTERR;
  }

  /* Check CRC */
  SPROX_ComputeIso14443BCrc(crc, &resp[0], (BYTE) (*resp_len - 2));
  if ((crc[0] != resp[*resp_len - 2]) || (crc[1] != resp[*resp_len - 1]))
  {
    /* Wrong CRC ! */
#ifdef _DEBUG
    printf("Wrong CRC : %02X%02X / %02X%02X\n", crc[0], crc[1],
           resp[*resp_len - 2], resp[*resp_len - 1]);
#endif
    return MI_CRCERR;
  }

  *resp_len = *resp_len - 2;

  return MI_OK;
}

SWORD Jewel_Transmit(IN BYTE which, IN BYTE value, OUT BYTE * resp,
                     IN OUT BYTE * resp_len)
{
  SWORD rc;
  WORD resp_len_w;

  /*
   * Configure the RC531 :
   * - no parity, no CRC
   * - don't care for framing errors
   */

  switch (which)
  {
    case 0:                    /* First byte -> 7 bits only */
      WriteRIC(RegBitFraming, 0x07);
      /* No break, go on */

    case 1:                    /* Other byte */
      rc = ExchangeByteStream(PCD_TRANSMIT, &value, 1, NULL, NULL);
      break;

    case 2:                    /* Last byte */
      if (resp == NULL)
        return MI_LIB_CALL_ERROR;
      if (resp_len == NULL)
        return MI_LIB_CALL_ERROR;

      resp_len_w = *resp_len;

      rc = ExchangeByteStream(PCD_TRANSCEIVE, &value, 1, resp, &resp_len_w);

      *resp_len = (BYTE) resp_len_w;
      break;

    default:
      return MI_WRONG_PARAMETER_VALUE;
  }

  return rc;
}
